﻿using System;
using System.Collections.Concurrent;
using System.Reflection;
using System.Text;

namespace Framework.Common
{
    public static class Formatter
    {
        #region const
        const char LeftAngleQuote = '<';
        const char RightAngleQuote = '>';

        const char LeftCurlyBrace = '{';
        const char RightCurlyBrace = '}';

        const char LeftParenthesis = '(';
        const char RightParenthesis = ')';

        const char DefaultSeperator = ',';
        const string MethodOfTypeChar = "::";
        #endregion

        #region format type
        /// <summary>
        /// get type format string
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private static StringBuilder GetTypeFormatString(Type type)
        {
            var stringBuilder = new StringBuilder();
            stringBuilder.Append(type.FullName);
            if (type.IsGenericTypeDefinition)
            {
                stringBuilder.Append(LeftAngleQuote);
                Type[] argumentsTypes = type.GetGenericArguments();
                for (int i = 0; i < argumentsTypes.Length; i++)
                {
                    if (i > 0)
                        stringBuilder.Append(DefaultSeperator);
                    stringBuilder.Append(LeftCurlyBrace);
                    stringBuilder.Append(argumentsTypes[i]);
                    stringBuilder.Append(RightCurlyBrace);
                }
                stringBuilder.Append(RightAngleQuote);
            }
            return stringBuilder;
        }
        #endregion

        #region format method

        /// <summary>
        /// get method full name with args
        /// </summary>
        /// <param name="method"></param>
        /// <returns></returns>
        public static string GetMethodFormatString(MethodBase method)
        {
            Type methodType = method.DeclaringType;
            StringBuilder stringBuilder = GetTypeFormatString(methodType);
            stringBuilder.Append(MethodOfTypeChar);
            stringBuilder.Append(method.Name);
            if (method.IsGenericMethodDefinition || method.IsGenericMethod)
            {
                stringBuilder.Append(LeftAngleQuote);
                Type[] argumentsTypes = method.GetGenericArguments();
                for (int i = 0; i < argumentsTypes.Length; i++)
                {
                    if (i > 0)
                        stringBuilder.Append(DefaultSeperator);
                    stringBuilder.Append(argumentsTypes[i]);
                }
                stringBuilder.Append(RightAngleQuote);
            }

            ParameterInfo[] parameters = method.GetParameters();
            stringBuilder.Append(LeftParenthesis);
            for (int i = 0; i < parameters.Length; i++)
            {
                if (i > 0) stringBuilder.Append(DefaultSeperator);

                stringBuilder.Append(LeftCurlyBrace);
                stringBuilder.Append(i);
                stringBuilder.Append(RightCurlyBrace);
            }
            stringBuilder.Append(RightParenthesis);
            return stringBuilder.ToString();
        }

        /// <summary>
        /// Format
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="args"></param>
        /// <param name="method"></param>
        /// <returns></returns>
        public static string Format(Object instance, Object[] args, MethodBase method)
        {
            string methodFormat = MethodDict.GetOrAdd(method, GetMethodFormatString);
            return Format(instance, args, methodFormat);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="args"></param>
        /// <param name="methodFormatString"></param>
        /// <returns></returns>
        public static string Format(Object instance, Object[] args, string methodFormatString)
        {
            var sb = new StringBuilder();
            if (args != null)
            {
                sb.AppendFormat(methodFormatString, args);
            }
            else
            {
                sb.Append(methodFormatString);
            }
            if (instance != null)
            {
                sb.Append(LeftParenthesis).Append(instance).Append(RightParenthesis);
            }
            return sb.ToString();
        }

        #endregion

        #region constuctor
        static Formatter()
        {
            MethodDict = new ConcurrentDictionary<MethodBase, string>();
        }
        #endregion

        #region private static member
        static readonly ConcurrentDictionary<MethodBase, string> MethodDict;
        #endregion
    }
}
